'''
Created on 08-02-2012

Resolver el PM 0-1 mediante ACE.

@author: cleve
'''
from pyaceOperations.matrix import MatrixClass
from random import randint, choice
from operator import itemgetter
from copy import deepcopy
import threading

from progressbar import Percentage, ProgressBar, Bar, ETA
from numpy.lib.scimath import sqrt

class BusquedaDeItems(object):
    '''
    classdocs
    '''


    def __init__ (self, masa_particulas, universos, particulasDetectoras, acelerar, listaElementos, maximaCapacidad):
        """ Iniciar la clase """
        
        #Utilizado unicamente para resolver el problema de la mochila
        self.pesoMaximo = maximaCapacidad
        self.listaElementosEnMochila = []
        self.finLlenado = False
        
        self.c = 3e8
        self.acelerar = acelerar
        self.particulasDetectoras = particulasDetectoras
        self.universos = universos
        self.listaElementos = listaElementos
        self.nodos = len(self.listaElementos)
        self.matricesEspacioTemporales = MatrixClass(self.nodos, self.listaElementos, self.particulasDetectoras)
        self.mejoresItems = []
            
    
    def RunUniversos(self):
        """ Function doc
      
        @param None: None
        @return RETURN: DESCRIPTION
        """
        #Barra de progreso.
        self.widgets = ['ACE(KP):', Percentage(), ' ', Bar(marker='#',left='[',right=']'), ' ', ETA(), ' ']
        self.pbar = ProgressBar(widgets=self.widgets, maxval = self.universos).start()
        #Fin barra de progreso.
        
        mejoresElementosDeUniverso = []
                
        for universos in xrange(self.universos):
            
            #Creo espacio, este metodo recibe como parametro la dimension.
            curvatura = self.matricesEspacioTemporales.BigBang((1, self.nodos))
            mejoresElementosDeUniverso = []
        
            for particulaDetectora in xrange(self.particulasDetectoras):
              
                #Realizo una copia de los items disponibles para ir seleccionandolos segun heuristica.
                itemsDisponibles = deepcopy(self.listaElementos)
                
                #Variable que almacena pesos/utilidad dinamicamente hasta que llegue al maximo permitido.
                sumaPesosItems = 0.0
                sumaUtilidad = 0.0
                
                #Seleccionar items para realizar calculo.
                listaDeItems = []
                  
                #Calculo del campo gravitatorio para cada arista, luego se determina cual es la mejor.
                while 1:
                   
                    #Guardo resultado parcial para cada particula.
                    resultadoParcial = []
                    
                    #Calculo campo para cada item disponible.
                    for seleccionItem in itemsDisponibles:
                        '''
                        Calculo de campo gravitatorio: En esta seccion se define la heuristica.                        

                        '''
                        
                        #Masa en espacio-tiempo.
                        masaEspacioTiempo = float(curvatura[seleccionItem[2]])
                        
                        #Peso de elemento desde tupla.
                        w = seleccionItem[0]
                            
                        if self.acelerar == True:
                            calculo = (masaEspacioTiempo * 1)/(w * (1-randint(0,3e8)/3e8)**(2/3))
                        
                        else:
                            #velocidadPropia = randint(1,3e8)
                            calculo = masaEspacioTiempo / w

                        resultadoParcial.append((calculo, seleccionItem))
                        #Fin calculo de campo gravitatorio
                       
                    #Buscar la mejor seleccion, deja el mayor valor al final de la lista.
                    #Argumento "Reverse" para escoger el menor valor de acuerdo al grafo construido con
                    #razon u/w.
                    
                    resultadoParcial.sort(reverse = False)
                    
                    #Se elige el mejor item actual.
                    mejorItem = resultadoParcial[-1][1]
                    
                    #Remuevo item elegido.
                    itemsDisponibles.remove(mejorItem)
                    
                    #Sumo los pesos de los elementos seleccionados por la arista
                    sumaPesosItems = mejorItem[0] + sumaPesosItems
                    
                    #Sumo utilidadde elemento seleccionados
                    sumaUtilidad = mejorItem[1] + sumaUtilidad
                    
                    #Guardar identificador de elementos que se almacenan en mochila
                    self.listaElementosEnMochila.append(mejorItem)
                    
                    #Guardar identificador de primer elemento de arista para mostrarlos posteriormente.
                    listaDeItems.append(mejorItem[2])
                                        
                    #Si se supera capacidad de la mochila, entonces quitar elemento.
                    if sumaPesosItems > self.pesoMaximo:
                        #Si se supera el limite, resto el ultimo peso agregado
                        sumaPesosItems = sumaPesosItems - mejorItem[0]
                        
                        #Si se supera el limite, resto la utilidad calculada.
                        sumaUtilidad = sumaUtilidad - mejorItem[1]
                        
                        #Si se supera el limite, elimino el ultimo elemento agregado
                        self.listaElementosEnMochila.pop()
                        listaDeItems.pop()
                        
                    if len(itemsDisponibles) == 0 or sumaPesosItems == self.pesoMaximo:
                        break
                    
                mejoresElementosDeUniverso.append((sumaPesosItems, listaDeItems, sumaUtilidad))
                
                #Reinicio lista de itemsDisponibles.
                self.listaElementosEnMochila = []
                itemsDisponibles = []
                self.finLlenado = False
                
                #Modifico curvatura E-T
                curvatura = self.matricesEspacioTemporales.ModificarCurvaturaMochila(curvatura)
                
            #Ordeno segun mejores utilidades (mayor a menor).
            mejoresElementosDeUniverso = sorted(mejoresElementosDeUniverso, reverse=True, key=itemgetter(2))
                    
            #Guardo mejor utilidad con el conjunto de itemsDisponibles.
            self.mejoresItems.append((mejoresElementosDeUniverso[0][0], mejoresElementosDeUniverso[0][1], mejoresElementosDeUniverso[0][2]))
            
            #Optimizacion de memoria.
            self.mejoresItems = sorted(self.mejoresItems, reverse=True, key=itemgetter(2))[:2]        
            
            self.pbar.update(universos+1)        
            
        #Ordeno por utilidad
        self.mejoresItems = sorted(self.mejoresItems, reverse=True, key=itemgetter(2))
        
            
        
        
        self.pbar.finish()
        return self.mejoresItems[0]
        
